package com.hk.security.weixin.cp;

import com.hk.commons.util.StringUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpService;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;


/**
 * 微信回调过滤器
 *
 * @author kevin
 * @date 2018年2月8日上午11:18:31
 */
public class WeiXinCpCallbackAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    /**
     * <pre>
     *
     * 微信扫码登陆返回参数 code
     *
     * 查看文档 ：<a href="https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=b2aec2aaa65154f7df33d39101e4aedb61d8fef3&lang=zh_CN" />
     * </pre>
     */
    private static final String CODE_PARAM_NAME = "code";

    /**
     * <pre>
     * 微信扫码登陆返回参数 state
     *     如果用户有配置了此参数，微信会返回
     *     如果没有配置，则为空
     *     可防止csrf攻击（跨站请求伪造攻击）
     * 查看文档 ：<a href="https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=b2aec2aaa65154f7df33d39101e4aedb61d8fef3&lang=zh_CN" />
     * </pre>
     */
    private static final String STATE_PARAM_NAME = "state";

    /**
     * 微信 MpService
     */
    private final WxCpService wxCpService;

    /**
     * 配置
     */
    private final String state;

    public WeiXinCpCallbackAuthenticationFilter(WxCpService wxCpService,
                                                String callbackUrl,
                                                String state) {
        /* 处理 微信回调的url请求  */
        super(PathPatternRequestMatcher.withDefaults().matcher(callbackUrl));
        setAuthenticationDetailsSource(new WeiXinCpAuthenticationDetailsSource());
        this.wxCpService = wxCpService;
        this.state = state;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        final var code = request.getParameter(CODE_PARAM_NAME);
        final var requestState = request.getParameter(STATE_PARAM_NAME);
        if (StringUtils.isNotEmpty(this.state) && StringUtils.notEquals(this.state, requestState)) {
            throw new AuthenticationServiceException("登录失败，跨站请求伪造攻击");
        }
        if (StringUtils.isNotEmpty(code)) { // 用户同意授权
            try {
                var oAuth2Service = wxCpService.getOauth2Service();
                var userInfo = oAuth2Service.getUserInfo(code);
                var authenticationToken = new WeiXinCpAuthenticationToken(userInfo);
                setDetails(request, authenticationToken);
                return getAuthenticationManager().authenticate(authenticationToken);
            } catch (WxErrorException e) {
                if (logger.isErrorEnabled()) {
                    logger.error(STR."微信认证失败：\{e.getMessage()}", e);
                }
                throw new AuthenticationServiceException(e.getMessage());
            }
        }
        throw new AuthenticationServiceException("code is null");
    }

    private void setDetails(HttpServletRequest request, WeiXinCpAuthenticationToken authenticationToken) {
        authenticationToken.setDetails(authenticationDetailsSource.buildDetails(request));
    }

}
